﻿namespace MicroCloud.Swagger
{
    /// <summary>
    /// 接口文档模块(Swagger)基类
    /// </summary>
    [Description("接口文档模块(Swagger)")]
    [DependsOnPacks(typeof(AspNetCorePack))]
    public abstract class SwaggerPackBase : AspPackBase
    {
        //字段
        private MicroCloudOptions _options;
        private readonly List<GroupInfo> groupInfos = new()
        {
            //new GroupInfo{ Name="api", Title="接口文档", Description="未分组接口文档", Visible = true }
        };

        #region 属性
        /// <summary>
        /// 获取 模块级别
        /// <para>级别越小越先启动</para>
        /// </summary>
        public override PackLevel Level => PackLevel.Framework;
        
        /// <summary>
        /// 获取 模块启动顺序编号
        /// <para>模块启动的顺序先按级别启动，级别内部再按此顺序启动，级别默认为0，表示无依赖，需要在同级别有依赖顺序的时候，再重写为 >0 的顺序值。</para>
        /// </summary>
        public override int OrderNo => 0;

        #endregion

        #region 方法
        /// <summary>
        /// 将模块服务添加到依赖注入服务容器中
        /// </summary>
        /// <param name="services">依赖注入服务容器</param>
        /// <returns></returns>
        public override IServiceCollection AddServices(IServiceCollection services)
        {
            _options = services.GetMicroCloudOptions();
            if (_options?.Swagger?.Enabled != true)
            {
                return services;
            }

            services.AddMvcCore().AddApiExplorer();

            // 获取控制器接口信息
            var controllers = AssemblyManager.FindTypes(m => m.IsController() && (m.IsPublic || m.IsNestedAssembly));
            foreach (var controller in controllers)
            {
                var controllerApiExplorerAttributes = controller.GetAttributes<ApiExplorerAttribute>().ToArray();
                foreach (var apiExplorerAttribute in controllerApiExplorerAttributes)
                {
                    if (apiExplorerAttribute != null && !apiExplorerAttribute.GroupName.IsNullOrWhiteSpace())
                    {
                        if (!groupInfos.Exists(o => o.Name.ToLower() == apiExplorerAttribute.GroupName.ToLower()))
                        {
                            groupInfos.Add(new GroupInfo
                            {
                                Name = apiExplorerAttribute.GroupName.ToLower(),
                                Title = apiExplorerAttribute.Title.IsMissing() ? apiExplorerAttribute.GroupName : apiExplorerAttribute.Title,
                                Description = apiExplorerAttribute.Description,
                                Visible = true,
                            });
                        }
                    }
                }
            }

            //从配置文件中获取分组信息（显示的、隐藏的）
            if (_options?.Swagger?.Groups?.Count > 0)
            {
                foreach (var swaggerGroup in _options?.Swagger?.Groups)
                {
                    if (swaggerGroup.Name.IsMissing())
                    {
                        continue;
                    }
                    var groupInfo = groupInfos.FirstOrDefault(o => o.Name.ToLower() == swaggerGroup.Name.ToLower());
                    if (groupInfo != null)
                    {
                        if (!swaggerGroup.Title.IsNullOrWhiteSpace()) groupInfo.Title = swaggerGroup.Title;
                        if (!swaggerGroup.Description.IsNullOrWhiteSpace()) groupInfo.Description = swaggerGroup.Description;
                        if (!swaggerGroup.Url.IsNullOrWhiteSpace()) groupInfo.Url = swaggerGroup.Url;
                        if (swaggerGroup.Visible.HasValue) groupInfo.Visible = swaggerGroup.Visible.Value;
                    }
                }
            }

            services.AddSwaggerGen(options =>
            {
                // 解决相同类名会报错的问题
                options.CustomSchemaIds(type => type.FullName);
                foreach (var groupInfo in groupInfos)
                {
                    if (groupInfo.Visible)
                    {
                        options.SwaggerDoc($"{groupInfo.Name}", new OpenApiInfo() { Version = groupInfo.Name, Title = groupInfo.Title, Description = groupInfo.Description });
                    }
                }

                // 判断接口归于哪个分组
                options.DocInclusionPredicate((version, apiDescription) =>
                {
                    if (!apiDescription.TryGetMethodInfo(out MethodInfo method))
                    {
                        return false;
                    }

                    string[] versions = method.DeclaringType.GetAttributes<ApiExplorerSettingsAttribute>().Select(m => m.GroupName).ToArray();
                    return versions.Any(m => m.ToString() == version);
                });

                // 为操作分配“operationId”提供自定义策略
                options.CustomOperationIds(apiDesc =>
                {
                    var controllerAction = apiDesc.ActionDescriptor as ControllerActionDescriptor;
                    return $"{controllerAction.ControllerName}_{controllerAction.ActionName}";
                });

                // 配置注释文档
                string[] xmlPatterns = _options?.Swagger?.XmlPattern?.Split(',', StringSplitOptions.RemoveEmptyEntries);
                List<string> xmlFiles = xmlPatterns.SelectMany(pattern => Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, pattern))
                .Distinct().ToList();
                if (xmlFiles != null && xmlFiles.Count > 0)
                {
                    try
                    {
                        xmlFiles.ForEach(xml =>
                        {
                            options.IncludeXmlComments(xml, true);
                        });
                    }
                    catch { }
                }

                //定义界面授权属性
                options.AddSecurityDefinition("BearerToken", new OpenApiSecurityScheme()
                {
                    Description = "请输入带有Bearer的Token，形如 “Bearer {Token}”",
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey,
                    BearerFormat = "JWT",
                    Scheme = "Bearer"
                });

                if (_options?.Swagger?.UI.ToLower() == "swagger")
                {
                    //定义授权策略逻辑
                    options.AddSecurityRequirement(new OpenApiSecurityRequirement
                    {
                        {
                            new OpenApiSecurityScheme
                            {
                                Name = "Authorization",
                                Description = "请输入带有Bearer的Token，形如 “Bearer {Token}”",
                                Type = SecuritySchemeType.ApiKey,
                                In = ParameterLocation.Header,
                                Reference = new OpenApiReference
                                {
                                    Type = ReferenceType.SecurityScheme,
                                    Id = "BearerToken"
                                },
                            },
                            new string[] { "readAccess", "writeAccess" }
                        }
                    });

                    //将性能分析监测信息从后端发送到Swagger界面-测试用
                    //options.DocumentFilter<InjectMiniProfiler>();
                }
                else if (_options?.Swagger?.UI.ToLower() == "knife4j")
                {
                    //定义授权策略逻辑
                    options.OperationFilter<Knife4jFilter>();
                }

                options.DocumentFilter<HiddenApiFilter>();
            });

            services.AddSwaggerGenNewtonsoftSupport();

            return services;
        }

        /// <summary>
        /// 应用AspNetCore的服务业务
        /// </summary>
        public override void UsePack(WebApplication app)
        {
            if (_options?.Swagger?.Enabled != true) return;

            SwaggerOption swagger = _options.Swagger;
            app.UseSwagger(c =>
            {
                c.PreSerializeFilters.Add((doc, _) =>
                {
                    // 清除所有Server
                    doc.Servers?.Clear();
                });
            });
            // 配置UI
            if (swagger.UI.ToLower() == "swagger")
            {
                // 配置原生UI
                app.UseSwaggerUI((Action<SwaggerUIOptions>)(options =>
                {
                    if (swagger.IsHideSchemas)
                    {
                        options.DefaultModelsExpandDepth(-1);
                    }
                    foreach (var groupInfo in groupInfos)
                    {
                        if (groupInfo.Visible)
                        {
                            var endpointUrl = groupInfo.Url;
                            if (StringExtensions.IsNullOrWhiteSpace(endpointUrl))
                            {
                                if (StringExtensions.IsNullOrWhiteSpace(options.RoutePrefix))
                                {
                                    endpointUrl = $"/{groupInfo.Name}/swagger.json";
                                }
                                else
                                {
                                    endpointUrl = $"/{options.RoutePrefix}/{groupInfo.Name}/swagger.json";
                                }
                            }
                            options.SwaggerEndpoint(endpointUrl, groupInfo.Title);
                        }
                    }

                    if (!StringExtensions.IsNullOrWhiteSpace(swagger.RoutePrefix))
                    {
                        options.RoutePrefix = swagger.RoutePrefix.ToLower();
                    }

                    if (swagger.MiniProfiler)
                    {
                        options.IndexStream = () => GetType().Assembly.GetManifestResourceStream("MicroCloud.Swagger.Swagger.UI.index_miniprofiler.html");
                    }
                }));
            }
            else if (swagger.UI.ToLower() == "knife4j")
            {
                // 配置第三方UI（Knife4jUI）
                app.UseKnife4jUI((Action<Knife4jOptions>)(options =>
                {
                    if (swagger.IsHideSchemas)
                    {
                        options.DefaultModelsExpandDepth(-1);
                    }

                    foreach (var groupInfo in groupInfos)
                    {
                        if (groupInfo.Visible)
                        {
                            var endpointUrl = groupInfo.Url;
                            if (StringExtensions.IsNullOrWhiteSpace(endpointUrl))
                            {
                                if (StringExtensions.IsNullOrWhiteSpace(options.RoutePrefix))
                                {
                                    endpointUrl = $"/{groupInfo.Name}/swagger.json";
                                }
                                else
                                {
                                    //endpointUrl = $"/{options.RoutePrefix}/{groupInfo.Name}/swagger.json";
                                    endpointUrl = $"/{groupInfo.Name}/swagger.json";
                                }
                            }
                            options.SwaggerEndpoint(endpointUrl, groupInfo.Title);
                        }
                    }
                    options.MiniProfiler = swagger.MiniProfiler;

                }));
            }

            IsEnabled = true;
        }
        #endregion

    }
}